#ifndef GETCAMERAFRAME_THREAD_H
#define GETCAMERAFRAME_THREAD_H

#include <QThread>
#include <QSharedMemory>
#include <opencv2/opencv.hpp>
#include <QDebug>
#include <QSettings>
#include <QFile>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <QThread>

#ifdef Q_OS_WIN
#else
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#endif

struct CameraParams {
    CameraParams(){ memset(this, 0, sizeof(CameraParams)); }
    uint BRIGHTNESS; // 亮度，范围：0 ~ 127
    uint EXPOSURE_AUTO; // 曝光模式，0：自动曝光；1：手动曝光；2：快门优先曝光；3：光圈优先曝光
    uint EXPOSURE_ABSOLUTE; // 曝光值，范围：78 - 10000(不同型号的摄像头，范围不同)
    uint AUTO_WHITE_BALANCE; // 是否自动白平衡，0：手动； 1：自动
    uint BALANCE_TEMPERATURE; // 白平衡值，范围 2800~6500
};

static QSharedMemory* g_pShareVideo = nullptr;

class GetCameraFrame_Thread : public QThread
{
    Q_OBJECT
public:
    GetCameraFrame_Thread(QObject *parent = nullptr)
        : QThread(parent){
        m_bCameraOpen = false;
        m_bExit = true;
        this->start();
    }
    /************************************************************
     * 以下为使用 Ubuntu 驱动文件命令调节 操作/dev/video0文件，通过命令控制
     * 1. #include <fcntl.h>, int open(const char *device_name, int flags)
     * 2. #include <unistd.h>, int close(int fd)
     * 3. VIDIOC_G_CTRL - VIDIOC_S_CTRL - 获取或设置控件的当前值
     *      int ioctl(int fd, VIDIOC_G_CTRL, struct v4l2_control *argp)
     *      int ioctl(int fd, VIDIOC_S_CTRL, struct v4l2_control *argp)
     *      struct v412_control {
     *          __u32 id;  // 标识由应用程序设置的控件
     *          __s32 value; // 新值或当前值
     *      };
     *
     ************************************************************/
    //获取摄像头当前的一些设置
    void GetCurrentCameraParams(CameraParams *argp) {
#ifdef Q_OS_WIN
#else
        int fd = ::open("/dev/video0", O_RDWR, 0);
        if (fd > 0) {
            struct v4l2_control CurSetting;
            // 获取当前设置的曝光模式
            CurSetting.value = -1; CurSetting.id = V4L2_CID_EXPOSURE_AUTO; ioctl(fd, VIDIOC_G_CTRL, &CurSetting);
            // CurSetting.value: 0：自动曝光；1：手动曝光；2：快门优先曝光；3：光圈优先曝光
            argp->EXPOSURE_AUTO = CurSetting.value;

            // 获取曝光度
            CurSetting.value = -1; CurSetting.id = V4L2_CID_EXPOSURE_ABSOLUTE; ioctl(fd, VIDIOC_G_CTRL, &CurSetting);
            // CurSetting.value: 曝光度范围 78 - 10000(不同型号的摄像头，范围不同)
            argp->EXPOSURE_ABSOLUTE = CurSetting.value;

            // 获取亮度
            CurSetting.value = -1; CurSetting.id = V4L2_CID_BRIGHTNESS; ioctl(fd, VIDIOC_G_CTRL, &CurSetting);
            // CurSetting.value: 亮度范围：0 ~ 127
            argp->BRIGHTNESS = CurSetting.value;

            // 获取是否自动白平衡
            CurSetting.value = -1; CurSetting.id = V4L2_CID_AUTO_WHITE_BALANCE; ioctl(fd, VIDIOC_G_CTRL, &CurSetting);
            // CurSetting.value: 0：手动； 1：自动
            argp->AUTO_WHITE_BALANCE = CurSetting.value;

            // 获取白平衡的值
            CurSetting.value = -1; CurSetting.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE; ioctl(fd, VIDIOC_G_CTRL, &CurSetting);
            // CurSetting.value: 白平衡范围 2800~6500
            argp->BALANCE_TEMPERATURE = CurSetting.value;

            close(fd);
        }
#endif
    }
    //设置摄像头参数
    //type:  1=亮度;  2=曝光模式； 3=曝光度； 4：自动白平衡；     5：白平衡值;
    //value：亮度值； 0/1/2/3；      曝光度；   0：手动，1：自动；  白平衡值；
    void SetCameraParams(int type ,int value){
#ifdef Q_OS_WIN
#else
        int fd = ::open("/dev/video0", O_RDWR, 0);
        if (fd > 0) {
            struct v4l2_control Setting;
            if(1 == type){
                // 设置亮度值, value: 亮度范围：0 ~ 127
                Setting.id = V4L2_CID_BRIGHTNESS;
            }else if(2 == type){
                // 设置曝光模式, value: 0：自动曝光；1：手动曝光；2：快门优先曝光；3：光圈优先曝光
                Setting.id = V4L2_CID_EXPOSURE_AUTO;
            }else if(3 == type){
                //设置曝光度
                Setting.id = V4L2_CID_EXPOSURE_ABSOLUTE;
            }else if(4 == type){
                //设置自动白平衡, value: 0：手动 1：自动
                Setting.id = V4L2_CID_AUTO_WHITE_BALANCE;
            }else if(5 == type){
                //设置白平衡值, value: 白平衡范围 2800~6500
                Setting.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
            }
            Setting.value = value;
            ioctl(fd, VIDIOC_S_CTRL, &Setting);
            close(fd);
        }
#endif
    }

    void run()
    {
        // 打开摄像头
        QThread::sleep(1);
        if(m_cvCapture.isOpened()){
            m_cvCapture.release();
        }
        m_bCameraOpen = m_cvCapture.open(0);
        qDebug() << "camera open result=================" << m_bCameraOpen;
        if(m_cvCapture.isOpened()){
            m_cvCapture.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));
            m_cvCapture.set(CV_CAP_PROP_FRAME_WIDTH, 2048);
            m_cvCapture.set(CV_CAP_PROP_FRAME_HEIGHT,1536);
        }
        // 创建共享内存
        if(m_bCameraOpen){
            //
            if(nullptr == g_pShareVideo){
                g_pShareVideo = new QSharedMemory("Robot_Camera_Live");
                g_pShareVideo->create(2048*1536*3+5);
            }

            while (m_bExit) {
                cv::Mat matFrame;
                m_cvCapture.read(matFrame);
                cv::flip(matFrame, matFrame, -1);
                cv::cvtColor(matFrame, matFrame, CV_BGR2RGB);

                if(!g_pShareVideo->isAttached()){
                    g_pShareVideo->attach();
                }
                g_pShareVideo->lock();
                int *pMemData = (int*)g_pShareVideo->data(); //may be null
                pMemData[0] = matFrame.cols;
                pMemData[1] = matFrame.rows;
                int nFlag = matFrame.channels() * 8;
                size_t nBytes = matFrame.cols * matFrame.rows * nFlag / 8;
                memcpy((void*)(pMemData+2), (void*)matFrame.data, nBytes);
                g_pShareVideo->unlock();
            }

            m_cvCapture.release();
        }
        qDebug() << "摄像头线程退出";
    }

    cv::VideoCapture m_cvCapture;
    bool m_bCameraOpen;
    bool m_bExit;

#if 0
    //注释掉的代码均为OpenCV参数调节
    //摄像头参数恢复出厂设置
    void CameraParamRestoreFactory(){
        m_cvCapture.set(cv::CAP_PROP_BRIGHTNESS, 0); //亮度
        m_cvCapture.set(cv::CAP_PROP_CONTRAST, 0); //对比度
        m_cvCapture.set(cv::CAP_PROP_SATURATION, 0.35999999999999999); //饱和度
        m_cvCapture.set(cv::CAP_PROP_HUE, 0.5); //色调
        m_cvCapture.set(cv::CAP_PROP_GAIN, 0.20833333333333334); //增益
        m_cvCapture.set(cv::CAP_PROP_AUTO_EXPOSURE, 0.75); //设置为自动曝光

        QFile file("tmp_camera_param.ini");
        if (file.open(QIODevice::WriteOnly)) {
            file.close();
        }
        QString szCfg = QString("tmp_camera_param.ini");
        QSettings CfgSet(szCfg, QSettings::IniFormat);
        CfgSet.setIniCodec("UTF-8");
        CfgSet.setValue("CameraParam/brightness", QVariant(0));
        CfgSet.setValue("CameraParam/contrast", QVariant(0));
        CfgSet.setValue("CameraParam/saturation", QVariant(0.35999999999999999));
        CfgSet.setValue("CameraParam/hue", QVariant(0.5));
        CfgSet.setValue("CameraParam/gain", QVariant(0.20833333333333334));
        CfgSet.setValue("CameraParam/exposure_params", QVariant("inf"));  //曝光度
        CfgSet.setValue("CameraParam/white_balance", QVariant(-1));
        CfgSet.setValue("CameraParam/bIsAutoExposure", QVariant(true));
    }
    //开机时，初始化摄像头参数，重启机器摄像头设置过的参数可能会变：读取摄像头参数，并写入文件记录
    void InitCameraParams(){
        if(!QFile::exists("tmp_camera_param.ini")){
            //手动曝光时才可调节，自动曝光时不能调节，但其他参数的调节不受影响。
            double brightness = m_cvCapture.get(cv::CAP_PROP_BRIGHTNESS); //亮度
            double contrast = m_cvCapture.get(cv::CAP_PROP_CONTRAST);  //对比度
            double saturation = m_cvCapture.get(cv::CAP_PROP_SATURATION); //饱和度
            double hue = m_cvCapture.get(cv::CAP_PROP_HUE); //色调
            double gain = m_cvCapture.get(cv::CAP_PROP_GAIN); //增益
            double exposure_params = m_cvCapture.get(cv::CAP_PROP_EXPOSURE); //曝光度
            double white_balance = m_cvCapture.get(cv::CAP_PROP_WHITE_BALANCE_BLUE_U); //白平衡(暂不支持)
            bool bIsAutoExposure = false;
            double Is_auto_exposure = m_cvCapture.get(cv::CAP_PROP_AUTO_EXPOSURE); //获取是否自动曝光，设置为0.25是手动曝光，设置0.75是自动曝光.
            if (fabs(0.75 - Is_auto_exposure) < 0.000001) {
                bIsAutoExposure = true;
            }
            QFile file("tmp_camera_param.ini");
            if (file.open(QIODevice::WriteOnly)) {
                file.close();
            }
            QString szCfg = QString("tmp_camera_param.ini");
            QSettings CfgSet(szCfg, QSettings::IniFormat);
            CfgSet.setIniCodec("UTF-8");
            CfgSet.setValue("CameraParam/brightness", QVariant(brightness));
            CfgSet.setValue("CameraParam/contrast", QVariant(contrast));
            CfgSet.setValue("CameraParam/saturation", QVariant(saturation));
            CfgSet.setValue("CameraParam/hue", QVariant(hue));
            CfgSet.setValue("CameraParam/gain", QVariant(gain));
            CfgSet.setValue("CameraParam/exposure_params", QVariant(exposure_params));
            CfgSet.setValue("CameraParam/white_balance", QVariant(white_balance));
            CfgSet.setValue("CameraParam/bIsAutoExposure", QVariant(bIsAutoExposure));
        }
    }
    //开机时：设置摄像头参数，并保存成文件
    void SetCameraParams(){
        QString szCfg = QString("tmp_camera_param.ini");
        QSettings CfgSet(szCfg, QSettings::IniFormat);
        CfgSet.setIniCodec("UTF-8");
        double brightness = CfgSet.value("CameraParam/brightness").toDouble();
        double contrast = CfgSet.value("CameraParam/contrast").toDouble();
        double saturation = CfgSet.value("CameraParam/saturation").toDouble();
        double hue = CfgSet.value("CameraParam/hue").toDouble();
        double gain = CfgSet.value("CameraParam/gain").toDouble();
        double exposure_params = CfgSet.value("CameraParam/exposure_params").toDouble();
        double white_balance = CfgSet.value("CameraParam/white_balance").toDouble();
        bool bIsAutoExposure = CfgSet.value("CameraParam/bIsAutoExposure").toBool();

        m_cvCapture.set(cv::CAP_PROP_BRIGHTNESS, brightness); //亮度
        m_cvCapture.set(cv::CAP_PROP_CONTRAST, contrast); //对比度
        m_cvCapture.set(cv::CAP_PROP_SATURATION, saturation); //饱和度
        m_cvCapture.set(cv::CAP_PROP_HUE, hue); //色调
        m_cvCapture.set(cv::CAP_PROP_GAIN, gain); //增益
        m_cvCapture.set(cv::CAP_PROP_EXPOSURE, exposure_params);  //曝光度
        //    m_cvCapture.set(cv::CAP_PROP_WHITE_BALANCE_BLUE_U, white_balance); //白平衡(暂不支持)
        //自动曝光，设置为0.25是手动曝光，设置0.75是自动曝光.
        if(bIsAutoExposure){
            m_cvCapture.set(cv::CAP_PROP_AUTO_EXPOSURE, 0.75);
        }else{
            m_cvCapture.set(cv::CAP_PROP_AUTO_EXPOSURE, 0.25);
        }
    }
    //摄像头参数调整
    //type: 1=亮度; 2=对比度; 3=饱和度; 4=色调; 5=增益; 6=曝光度; 7=是否自动曝光value=0为手动，1为自动;
    void SetCaptureValue(int type ,double value){
        QString szCfg = QString("tmp_camera_param.ini");
        QSettings CfgSet(szCfg, QSettings::IniFormat);
        CfgSet.setIniCodec("UTF-8");
        switch(type) {
        case 1: { m_cvCapture.set(cv::CAP_PROP_BRIGHTNESS, value); CfgSet.setValue("CameraParam/brightness", QVariant(value)); } break;
        case 2: { m_cvCapture.set(cv::CAP_PROP_CONTRAST, value); CfgSet.setValue("CameraParam/contrast", QVariant(value)); } break;
        case 3: { m_cvCapture.set(cv::CAP_PROP_SATURATION, value); CfgSet.setValue("CameraParam/saturation", QVariant(value)); } break;
        case 4: { m_cvCapture.set(cv::CAP_PROP_HUE, value); CfgSet.setValue("CameraParam/hue", QVariant(value)); } break;
        case 5: { m_cvCapture.set(cv::CAP_PROP_GAIN, value); CfgSet.setValue("CameraParam/gain", QVariant(value)); } break;
        case 6: { m_cvCapture.set(cv::CAP_PROP_EXPOSURE, value); CfgSet.setValue("CameraParam/exposure_params", QVariant(value)); } break;
        case 7: {
            if (fabs(value - 0) < 0.000001) {
                m_cvCapture.set(cv::CAP_PROP_AUTO_EXPOSURE, 0.25);
                CfgSet.setValue("CameraParam/bIsAutoExposure", QVariant(false));
            }else{
                m_cvCapture.set(cv::CAP_PROP_AUTO_EXPOSURE, 0.75);
                CfgSet.setValue("CameraParam/bIsAutoExposure", QVariant(true));
            }
        }
            break;
        default:break;
        }
    }
#endif
};

#endif // GETCAMERAFRAME_THREAD_H
